#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>

#define MOVETO(x,y) printf("\033[%d;%dH",(y),(x))
#define HEIGHT 20
#define WIDTH 60
#define GREEN "\033[0;32;24m"
#define RED  "\033[0;31;24m"
#define YELLOW "\033[0;33;24m"
#define BLUE "\033[0;34;24m"
#define PUEPLE "\033[0;35;24m"
#define NONE   "\033[0m"

//10,5    69,5
//10,24   69,24
typedef enum {UP=-1,DOWN=1,LEFT=2,RIGHT=4}MOVE_DIR;
int snake_lengh = 3;
int snake_changenew = 1;
int score ,read_score;
int t,temp = 1;
int read_snake_lengh,read_snake_changenew;
int stop_continue = 1;
FILE *fp;

MOVE_DIR cur_dir = RIGHT;

typedef struct snake_body         //save snake body
{
	int x;
	int y;
	MOVE_DIR dir ;
}body;
body b_init;

typedef struct ss                 //line
{
	body ss_body;
	struct ss *pnext;
}sss,*pss;
pss lhead,shead,tp,p;

typedef struct food_
{
	int x;
	int y;
	int time;
}s_food,*pfood;
pfood food ;
s_food b_food;

void init()
{
	
	char s[20],yes_no_usesave;
	int i;
	score = 0;
	srand(time(NULL));

	fp = fopen("snake_save3.dat","r");
	if (NULL == fp)
	{
		p = (pss)malloc(sizeof(sss));
		if ( NULL == p ) return ;
		p->ss_body.x = 30;
		p->ss_body.y = 10;
		lhead = tp = p;
		for (i=0;i<snake_lengh-1;i++)
		{	p = (pss)malloc(sizeof(sss));
			if ( NULL == p ) return ;
			p->ss_body.x = 31+i;
			p->ss_body.y = 10;
			tp->pnext = p;
			tp = p ;
		}
		p->pnext = NULL;
	
		food = (pfood)malloc(sizeof(pfood));
		if ( NULL == food ) return ;
		food->x = rand()%(WIDTH-2)+11;
		food->y = rand()%(HEIGHT-2)+6;
		food->time = rand()%20+20;
		return ;
			
	}
	if (fp) fclose(fp);

	fp = fopen("snake_save3.dat","r");
	if (NULL == fp)
	{
		perror ("open read");
		return  ;
	}
	fread(s,6,1,fp);
	
	fread(&read_snake_changenew,4,1,fp);
	fread(&read_snake_lengh,4,1,fp);
	fread(&read_score,4,1,fp);

	if (read_score == -1)          
	{

		p = (pss)malloc(sizeof(sss));
		if ( NULL == p ) return ;
		p->ss_body.x = 30;
		p->ss_body.y = 10;
		lhead = tp = p;
			for (i=0;i<snake_lengh-1;i++)
		{	p = (pss)malloc(sizeof(sss));
			if ( NULL == p ) return ;
			p->ss_body.x = 31+i;
			p->ss_body.y = 10;
			tp->pnext = p;
			tp = p ;
		}
		p->pnext = NULL;
		
		food = (pfood)malloc(sizeof(pfood));
		if ( NULL == food ) return ;
		food->x = rand()%(WIDTH-2)+11;
		food->y = rand()%(HEIGHT-2)+6;
		food->time = rand()%20+20;
		fclose(fp);
		return;
			
	}

	if (read_score != -1)
		printf("there is a game_save_file . input 'y' to continue or 'n' to play a new game !\n");
		scanf("%c",&yes_no_usesave);
	if (yes_no_usesave == 'y' || yes_no_usesave == 'Y')
	{	
		snake_changenew = read_snake_changenew;
		snake_lengh = read_snake_lengh ;
		score = read_score;

		p = (pss)malloc(sizeof(pss));
		fread(&b_init,sizeof(body),1,fp);
		p->ss_body = b_init;
		lhead = tp = p;
		for (i = 1;i < read_snake_lengh;i++)
		{
			p = (pss)malloc(sizeof(pss));
			fread(&b_init,sizeof(body),1,fp);
			p->ss_body = b_init;
			tp->pnext = p ;
			tp = p;
		}
		p->pnext = NULL;
		shead = p;
		cur_dir = shead->ss_body.dir;

		food = (pfood)malloc(sizeof(pfood));
		fread(food,sizeof(s_food),1,fp);
	}
	else 
	{
		p = (pss)malloc(sizeof(sss));
		if ( NULL == p ) return ;
		p->ss_body.x = 30;
		p->ss_body.y = 10;
		lhead = tp = p;
			for (i=0;i<snake_lengh-1;i++)
		{	p = (pss)malloc(sizeof(sss));
			if ( NULL == p ) return ;
			p->ss_body.x = 31+i;
			p->ss_body.y = 10;
			tp->pnext = p;
			tp = p ;
		}
		p->pnext = NULL;
		
		food = (pfood)malloc(sizeof(pfood));
		if ( NULL == food ) return ;
		food->x = rand()%(WIDTH-2)+11;
		food->y = rand()%(HEIGHT-2)+6;
		food->time = rand()%20+20;
		fclose(fp);
		return;
	}

		
	if (fp) fclose(fp);
		
	
}

void display()
{
	int i,j;
 //	printf(RED);
	MOVETO(10,5);
	printf("╔");
	MOVETO(69,5);
	printf("╗");
	MOVETO(10,24);
	printf("╚");
	MOVETO(69,24);
	printf("╝");
	MOVETO(11,5);
	for (i=0;i<WIDTH-2;i++)
		printf("═");
	for (i=1;i<HEIGHT-1;i++)
	{
		MOVETO(10,5+i);
		printf("║");
		MOVETO(10+WIDTH-1,5+i);
		printf("║");

	}
	MOVETO(11,5+HEIGHT-1);
	for (i=0;i<WIDTH-2;i++)
		printf("═");
 //	printf(NONE);
	MOVETO(food->x,food->y);
	printf("☃");
	p = lhead;
	while( p != NULL && p->pnext != NULL)
	{
		MOVETO(p->ss_body.x,p->ss_body.y);
		printf("❄");
		printf("\n");    //输出缓冲区的指令
		p = p->pnext;
	}
	MOVETO(p->ss_body.x,p->ss_body.y);
	printf("☢");
	printf("\n");
	printf(GREEN);
	MOVETO(10,25);
	printf("food time have : %d",food->time);
	MOVETO(10,26);       //输出缓冲区的指令
	printf("score is %d\n",score);
	printf(NONE);
	MOVETO(10,27);
	printf("input w s a d to move");
	printf("\n");
	MOVETO(10,28);
	printf("input f to stop or c to continue or q to quit !");
	printf("\n");
	
	MOVETO(0,35);
	printf("\n"); 
	printf("\33[?25l");       //隐藏光标

}

int snake_move()
{
	int i,j,lemp = 0,x,y;
	pss lp; 
	p = lhead ; 
	x = lhead -> ss_body.x;
	y = lhead -> ss_body.y;

	food->time--;

	if (stop_continue == 0)
		return 1;
	for (i = 0;i < snake_lengh-1;i++)
	{
		p->ss_body.x = p->pnext->ss_body.x;
		p->ss_body.y = p->pnext->ss_body.y;
		p = p->pnext;
	}
	shead = p ;
	if (cur_dir == RIGHT||cur_dir == LEFT)
	{
		shead->ss_body.x += cur_dir-3;
		shead->ss_body.dir = cur_dir;
	}
	else
	{
		shead->ss_body.y += cur_dir;
		shead->ss_body.dir = cur_dir;
	}
	if (shead->ss_body.x == 10||shead->ss_body.x == WIDTH+9||shead->ss_body.y==5||shead->ss_body.y==HEIGHT+4)
		return 0;
	p = lhead;
	for (j = 0;j < snake_lengh-1;j++)
	{
		if(p->ss_body.x == shead->ss_body.x && p->ss_body.y == shead->ss_body.y)
			return 0;
		p = p->pnext;
	}

	if (food->time == 0)
	{
		while(lemp == 0)
		{
			food->x = rand()%(WIDTH-2)+11;
			food->y = rand()%(HEIGHT-2)+6;
			food->time = rand()%20+20;
			p = lhead;
			for (j = 0;j < snake_lengh;j++)
			{
				if (p->ss_body.x==food->x&&p->ss_body.y==food->y)
				{
					lemp=0;
					break;
				}
				lemp=1;
				p = p->pnext;
			}
		}
	}

	if (shead->ss_body.x == food->x && shead->ss_body.y == food->y)
	{
		shead->ss_body.dir = cur_dir ;
		snake_lengh++;
		score++;
		lp = (pss)malloc(sizeof(sss));
		if ( NULL == lp ) return 0;
		lp->ss_body.x = x;
		lp->ss_body.y = y;
		lp->pnext = lhead;
		lhead = lp;
		while(lemp == 0)
		{
			food->x = rand()%(WIDTH-2)+11;
			food->y = rand()%(HEIGHT-2)+6;
			food->time = rand()%20+20;
			p = lhead;
			for (j = 0;j < snake_lengh;j++)
			{
				if (p->ss_body.x==food->x&&p->ss_body.y==food->y)
				{
					lemp=0;
					break;
				}
				lemp=1;
				p = p->pnext;
			}
		}

	}
		
 	
}

int kbhit()                    //实现读入指令和不停顿
{
	struct termios oldt,newt;
	int ch;
	int oldf;

	tcgetattr(STDIN_FILENO,&oldt);
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO,TCSANOW,&newt);
	oldf = fcntl(STDIN_FILENO,F_GETFL,0);
	fcntl(STDIN_FILENO,F_SETFL,oldf | O_NONBLOCK);

	ch = getchar();

	tcsetattr(STDIN_FILENO,TCSANOW,&oldt);
	fcntl(STDIN_FILENO,F_SETFL,oldf);
	if(ch != EOF)
		return ch;
	else 
		return 0;
}
void get_wsadq()
{

}


void save_game()
{
	
	int i;
	char s[20];
	fp = fopen("snake_save3.dat","w");
	if (NULL == fp)
	{
		perror ("open write");
		return ;
	}
	fwrite("SNAKEA",6,1,fp);
	fwrite(&snake_changenew,4,1,fp);
	fwrite(&snake_lengh,4,1,fp);
	fwrite(&score,4,1,fp);
	p = lhead;
	for (i = 0;i < snake_lengh;i++)
	{
		fwrite(&p->ss_body,sizeof(body),1,fp);
		p = p->pnext;
	}
	fwrite(food,sizeof(s_food),1,fp);

	if (fp) fclose(fp);
	printf("\33[?25h");             //显示光标

/*	fp = fopen("snake_save.dat","r");
	if (NULL == fp)
	{
		perror ("open read");
		return  ;
	}
	fread(s,6,1,fp);
	fread(&read_snake_changenew,4,1,fp);
	fread(&read_snake_lengh,4,1,fp);
	fread(&read_score,4,1,fp);
	printf("%s,%d,%d\n",s,read_snake_changenew,read_snake_lengh);
	for (i = 0;i < read_snake_lengh;i++)
	{
		fread(&b_init,sizeof(body),1,fp);
		printf("%d,%d\n",b_init.x,b_init.y);
	}
	fread(&b_food,sizeof(s_food),1,fp);
	printf("%d,%d\n",b_food.x,b_food.y);

	if (fp) fclose(fp);
*/
}


void main()
{	
	init();
	while(1)
	{
		switch(kbhit())        //向右爬行时输入向左指令不会退出
		{                      //向右爬行时输入向右指令会短暂加速
			case 'w':{
				if (stop_continue == 1)
				{
					if (cur_dir == DOWN)
						break;
					else if (cur_dir == UP)
					{
						temp += 2;break;
					}				
					else cur_dir = UP;break;
				}
				break;
			}
			case 's':{
				if (stop_continue == 1)
				{
					if (cur_dir == UP)
						break;
					else if (cur_dir == DOWN)
					{
						temp += 2;break;
					}
					else cur_dir = DOWN;break;
				}
				break ;
			}
			case 'd':{
				if (stop_continue == 1)
				{
					if (cur_dir == LEFT)
						break;
					else if (cur_dir == RIGHT)
					{
						temp += 2;break;
					}
					else cur_dir = RIGHT;break;
				}
				break;
			}
			case 'a':{
				if (stop_continue == 1)
				{
					if (cur_dir == RIGHT)
						break;
					else if (cur_dir == LEFT)
					{
						temp += 2;break;
					}
					else cur_dir = LEFT;break;
				} break;
			}
			case 'f':{
				stop_continue = 0;break;
			}
			case 'c':{
				stop_continue = 1;break;
			}
			case 'q':{
				save_game();
				return ;
			}
		}
	
		
	
		if(snake_move() == 0)
		{
			score = -1;
			save_game();		
			return ;
		}
		system("clear");
		display();
		if(score == 50)
		{
			MOVETO(10,24);
			printf(RED);
			printf("\ngame over.		\nYou Win!				\n");
			printf(NONE);
			score = -1;
			save_game();
			return ;
		}
		t = score/10+1;
		usleep(1000000/temp);
		temp = t;
		
	}
}
